﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Security.Cryptography;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Net;
using System.Diagnostics;

namespace Cryptophone2
{
    public partial class MakingCall : Form
    {

        private volatile bool _shouldStop;
        private int stato; //0 = call waiting, 1 = call accepted
        private volatile bool _chiudi;
        private bool fromForm;

        Thread doWait;
        Thread doEnd;

        string url;
        int direction;

        TcpListener tcpCallListener;
        Socket socketServer;
        NetworkStream networkStream;
        System.IO.StreamWriter streamWriter;
        System.IO.StreamReader streamReader;

        public MakingCall(string ipNumber, int dir)
        {
            InitializeComponent();

            url = ipNumber;
            direction = dir;
            doWait = new Thread(DoWait);
            doEnd = new Thread(DoEnd);

            _shouldStop = false;
            _chiudi = false;
            fromForm = false;
            stato = 0;

            //Start the thread
            doWait.Start();
        }

        /// <summary>
        /// This thread is used to exchange data for security keys and 
        /// start a new process that call pjmedia library.
        /// </summary>
        void DoWait()
        {
            StringBuilder url2 = new StringBuilder();
            url2.Append(url);

            //returnUrl is used by server to retrive client IP
            StringBuilder returnUrl = new StringBuilder(16);

            int resultCrea = Cryptophone2.MySocket.CreaSocket(url2, direction, returnUrl);

            if (resultCrea == 0 && _shouldStop != true)
            {
                if (direction == 0)
                {
                    // if Server change ip value with returnUrl
                    url2.Remove(0, url2.Length);
                    url2.Append(returnUrl.ToString());
                }

                StringBuilder key_TX = new StringBuilder(60);
                StringBuilder key_RX = new StringBuilder(60);
                StringBuilder key_Vocal = new StringBuilder(15);

                // Retrive security keys
                int resultScambio = Cryptophone2.MySocket.ScambioChiaviSocket(key_TX, key_RX, key_Vocal);

                // Close Sockets
                Cryptophone2.MySocket.ChiudiSocket();

                if (resultScambio == 0)
                {
                    //Start a new TcpListenr used to talk with pjmedia process
                    IPAddress localAddr = IPAddress.Any;
                    tcpCallListener = new TcpListener(localAddr, 3667);
                    tcpCallListener.Start();

                    // start pjmedia process
                    Process myProcess = new Process();
                    string myProgramPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
                    myProcess.StartInfo.FileName = myProgramPath + "\\PjMediaStarter.exe";
                    myProcess.Start();

                    socketServer = tcpCallListener.AcceptSocket();

                    if (socketServer.Connected)
                    {
                        string buffer = "";
                        networkStream = new NetworkStream(socketServer);
                        streamWriter = new System.IO.StreamWriter(networkStream);
                        streamReader = new System.IO.StreamReader(networkStream);

                        //Send ip to process
                        buffer = url2.ToString();
                        streamWriter.WriteLine(buffer);
                        streamWriter.Flush();

                        //Send keyTx to process
                        buffer = key_TX.ToString();
                        streamWriter.WriteLine(buffer);
                        streamWriter.Flush();

                        //Send keyRx to process
                        buffer = key_RX.ToString();
                        streamWriter.WriteLine(buffer);
                        streamWriter.Flush();

                        // Pick te return value of pjmedia call
                        buffer = streamReader.ReadLine();

                        int resultPjStart = Convert.ToInt32(buffer);

                        if (resultPjStart == 0)
                            UpdateSasBox(key_Vocal.ToString()); // ok
                        else
                            UpdateSasBox("Error"); //error

                    }

                }
                else
                    UpdateSasBox("Error");
            }
            else
                UpdateSasBox("Error");
        } // fine thread


        /// <summary>
        /// This thread is used to close call after a STOP message send from call partner
        /// </summary>
        void DoEnd()
        {
            while (!_shouldStop)
            {
                if (_chiudi)
                {
                    CloseForm();
                }
            }
        }

        /// <summary>
        /// Change the stop variable with true
        /// </summary>
        void RequestStop()
        {
            _shouldStop = true;
        }

        /// <summary>
        /// This functin change text box with SAS code if success
        /// Write an error if not success
        /// </summary>
        /// <param name="value">SAS code</param>
        public void UpdateSasBox(string value)
        {
            if (InvokeRequired)
            {
                this.Invoke(new Action<string>(UpdateSasBox), new object[] { value });
                return;
            }
            textBoxSAS.Text = "SAS: " + value;

            if (!value.Equals("Error"))
            {
                stato = 1; //call Accepted

                doEnd.Start(); //Start doEnd Thread
            }
            else
            {
                stato = 0; // Errore 
            }

        }

        /// <summary>
        /// Click on close menu
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuItemClose_Click(object sender, EventArgs e)
        {
            _chiudi = true;
            CloseForm();
        }

        /// <summary>
        /// Public method used by graphic main thread to close call activity after STOP command
        /// </summary>
        public void setChiudi() {
            // metodo che serve per interrompere le chiamate dall'esterno.
            _chiudi = true;
            fromForm = true;
        }

        /// <summary>
        /// Function used to release resources and close the form.
        /// </summary>
        private void CloseForm()
        {
            if (InvokeRequired)
            {
                try
                {
                    this.Invoke(new Action(CloseForm));
                    return;
                }
                catch(ObjectDisposedException)
                {
                }
                
            }

            string buffer = "QUIT";

            if (stato == 0)
            {
                RequestStop();

                try
                {
                    //This command is used to close pjmedia process
                    streamWriter.WriteLine(buffer);
                }
                catch (Exception)
                {
                }
                

                this.DialogResult = DialogResult.Cancel;
                this.Close();
            }
            else
            {
                RequestStop();

                try
                {
                    streamWriter.WriteLine(buffer);
                    streamWriter.Flush();

                    //Rilascio le risorse
                    streamReader.Close();
                    streamWriter.Close();
                    networkStream.Close();
                    socketServer.Shutdown(SocketShutdown.Both);
                    socketServer.Close();
                    tcpCallListener.Stop();
                }
                catch (Exception)
                {
                }
                
                if(fromForm == true)
                    this.DialogResult = DialogResult.Cancel;
                else
                    this.DialogResult = DialogResult.Abort;

                this.Close();
            }
        }

    }
}